 
#include "ackqueue.h"
#include "cope.h"

NonAckQueue::NonAckQueue() {
    non_ack_list_ = new list<non_ack_t>;
}

/*
 * Is there any problem caused by deleting the same memory several times?
 */
NonAckQueue::~NonAckQueue() {
    non_ack_list_->clear();
    delete non_ack_list_;
}

entry_iter_t NonAckQueue::lookup_entry(int nodeid, u_int16_t local_seqnum) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non == non_ack_list_->end())
	return (entry_iter_t)NULL;
    entry_iter_t it_entry = it_non->entries->begin();
    for(; it_entry != it_non->entries->end(); ++it_entry) {
	if(it_entry->local_seqnum == local_seqnum)
	    return it_entry;
    }
    return it_entry;
}

non_iter_t NonAckQueue::lookup_node(int nodeid) {
    non_iter_t it_non = non_ack_list_->begin();
    for(; it_non != non_ack_list_->end(); ++it_non) {
	if(it_non->nodeid == nodeid)
	    return it_non;
    }
    return it_non;
}

bool NonAckQueue::has_node(int nodeid) {
    return lookup_node(nodeid) == non_ack_list_->end() ? false : true;
}


void NonAckQueue::enqueue_node(int nodeid, u_int16_t local_seqnum, Packet* p) {
    non_ack_t non_node;
    non_ack_entry_t non_entry;
    non_node.entries = new list<non_ack_entry_t>;
    non_entry.local_seqnum = local_seqnum;
    non_entry.rcounter = 0;
    non_entry.ppkt = p;
    non_node.entries->push_front(non_entry);

    non_node.nodeid = nodeid;
    non_ack_list_->push_front(non_node);
}

void NonAckQueue::enqueue_entry(int nodeid, u_int16_t local_seqnum, Packet* p,
				non_iter_t it_non) {
    non_ack_entry_t non_entry;
    non_entry.local_seqnum = local_seqnum;
    non_entry.rcounter = 0;
    non_entry.ppkt = p;
    // non_iter_t it_non = lookup_node(nodeid);
    it_non->entries->push_front(non_entry);
}


/*
 * 1. New nodeid: firstly enqueue the new node, and then enqueue the packet
 * 2. New packet: firstly find the existed node, and then enqueue the packet
 * 3. Old packet: it's a retransmision packet, update the rcounter by adding 1
 */
u_int8_t NonAckQueue::enqueue(int nodeid, u_int16_t local_seqnum, Packet* p) {
    entry_iter_t it_entry;
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non != non_ack_list_->end()) {
	it_entry = lookup_entry(nodeid, local_seqnum);
	if(it_entry == it_non->entries->end()) { // 2. New packet
	    enqueue_entry(nodeid, local_seqnum, p, it_non);
	} else { // 3. Old packet
	    // update_entry(nodeid, local_seqnum);
	    it_entry->rcounter++;
	    Packet::free(p);
	    return it_entry->rcounter;
	}
    } else { // 1. New nodeid
	enqueue_node(nodeid, local_seqnum, p);
    }
    return 0;
}

void NonAckQueue::showNonAck() {
    non_iter_t it_non = non_ack_list_->begin();
    for(; it_non != non_ack_list_->end(); ++it_non) {
	cout << "#########nodeid: " << it_non->nodeid << endl;
	entry_iter_t it_entry = it_non->entries->begin();
	for(; it_entry != it_non->entries->end(); ++it_entry) {
	    cout << " local: " << it_entry->local_seqnum
		 << " rc: " << (int)it_entry->rcounter
		 << " uid: " << HDR_CMN(it_entry->ppkt)->uid_
		 << " seqno: " << HDR_TCP(it_entry->ppkt)->seqno()
		 << " pktid: " << HDR_CMN(it_entry->ppkt)->cope_pkt_id()
		 << endl;
	}
    }
}

/*
 * The counter should be increased by 1 after get_packet be called each time.
 * We should check the counter after this function be called,
 * if the rcounter > 2, then delete this packet.
 */
u_int8_t NonAckQueue::get_packet(int nodeid, u_int16_t local_seqnum, Packet** p) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non == non_ack_list_->end()) {
	*p = NULL;
	return 0;
    }

    entry_iter_t it_entry = it_non->entries->begin();
    for(; it_entry != it_non->entries->end(); ++it_entry) {
	if(it_entry->local_seqnum == local_seqnum) {
	    *p = it_entry->ppkt;
	    it_entry->rcounter ++;
	    return it_entry->rcounter;
	}
    }
    
    *p = NULL;
    return 0;
}

//u_int8_t NonAckQueue::get_rc(int nodeid, u_int16_t local_seqnum) {}

u_int8_t NonAckQueue::lookup_packet(int nodeid, u_int16_t local, Packet** p) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non == non_ack_list_->end()) {
	*p =  NULL;
	return 0;
    }

    entry_iter_t it_entry = it_non->entries->begin();
    for(; it_entry != it_non->entries->end(); ++it_entry) {
	if(it_entry->local_seqnum == local) {
	    *p = it_entry->ppkt;
	    return it_entry->rcounter;
	}
    }

    *p = NULL;
    return 0;    
}

u_int16_t NonAckQueue::get_local_seq(int nodeid, int pktid) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non == non_ack_list_->end())
	return 0;
    entry_iter_t it = it_non->entries->begin();
    for(; it != it_non->entries->end(); ++it) {
	if(HDR_CMN(it->ppkt)->cope_pkt_id() == pktid)
	    return it->local_seqnum;
    }
    return 0;
}

non_ack_entry_t NonAckQueue::get_entry(int nodeid, u_int16_t local_seqnum) {
    entry_iter_t it_entry = lookup_entry(nodeid, local_seqnum);
    it_entry->rcounter++;
    return *it_entry;
}

void NonAckQueue::update_entry(int nodeid, u_int16_t local_seqnum) {
    entry_iter_t it_entry = lookup_entry(nodeid, local_seqnum);
    it_entry->rcounter++;
}

void NonAckQueue::delete_node(int nodeid) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non != non_ack_list_->end()) {
	non_ack_list_->erase(it_non);
    }
}

/*
 * If no entry after delete operation, should delete the node?
 * I am not sure if this would cause big overhead, if the network is very good.
 */
void NonAckQueue::delete_entry(int nodeid, u_int16_t local_seqnum, bool ack) {
    non_iter_t it_non = lookup_node(nodeid);
    if(it_non != non_ack_list_->end()) {
	entry_iter_t it_entry = lookup_entry(nodeid, local_seqnum);
	if(it_entry != it_non->entries->end()) {
	    if(!ack)
		it_entry->ppkt->setFlag(false);
	    else {
	  
		free(it_entry->ppkt);
		it_entry->ppkt = NULL;
	    }

	    it_non->entries->erase(it_entry);
	}
    }
}

AckPendQueue::AckPendQueue() {
    ack_pend_list_ = new list<ack_pend_t>;
    shift_num_ = 0;
}

AckPendQueue::~AckPendQueue() {
    ack_pend_list_->clear();
    delete ack_pend_list_;
}

/*
 * To lookup a specified neighbor in AckPendQueue, if the neighbor is already
 * in the queue, then return the iterator to its ack_pend_t, otherwise return
 * the iterator to the end of ack_pend_list_.
 */
pend_iter_t AckPendQueue::lookup_pend(int neighbor) {
    pend_iter_t it = ack_pend_list_->begin();
    for(; it != ack_pend_list_->end(); ++it) {
	if(it->neighbor == neighbor)
	    return it;
    }
    return it;
}

bool AckPendQueue::has_pend(int neighbor) {
    if(lookup_pend(neighbor) != ack_pend_list_->end())
	return true;
    return false;
}

void AckPendQueue::delete_pend(int neighbor) {
    pend_iter_t it = lookup_pend(neighbor);
    if(it != ack_pend_list_->end()) {
	ack_pend_list_->erase(it);
    }
}

/*
 * 1. New neighbor: enqueue this new neighbor, and then enqueue the pend
 * 2. New pend: find the existing neighbor, and then enqueue the pend
 * 3. Old pend: the sender hasn't received the ack, so retransmit the
 *    data packet. Retransmit this ack info if the rtimes < RT_TIMES.
 * 4. local sequence number =< shifts: that means this ack packet comes
 *    after we remove it from ackmap. Ignore it.
 */
void AckPendQueue::enqueue_pend(int neighbor, u_int16_t local, bool set, COPE* cope) {
    Scheduler& s = Scheduler::instance();
    pend_iter_t it = lookup_pend(neighbor);

    if(local == 642)
	cout << "";
    // incoming ack has been ACKed more than two times, ignore it
    if(it != ack_pend_list_->end() && local < it->shifts) { // =? <= ?
	return;
    }
    
    if(it == ack_pend_list_->end()) { // 1. New neighbor
	// cout << (int)local
	//      << " " << cope->node_->nodeid()
	//      << " " << it->ackmap
	//      << endl;

	ack_pend_t entry;
	entry.neighbor = neighbor;
	entry.lastack = local;
	entry.shifts = 0;
	entry.ackmap.set(ACKMAP_TOTAL_SIZE - local);
	ack_pend_list_->push_back(entry);
	
	// if local - shifts > 8, triger an event to send ackmap
	if(local - it->shifts > ACKMAP_SIZE) {
	    // cope->h_ack_pend_.neighbor_ = neighbor;
	    s.schedule(&cope->h_ack_pend_, &cope->pend_event_, 0.0);
	}	
    } else {
	// cout << (int)local
	//      << " " << it->shifts
	//      << " " << cope->node_->nodeid()
	//      << " " << it->ackmap
	//      << endl;
	if(it->ackmap[ACKMAP_TOTAL_SIZE - local + it->shifts] == 0) { // 2. New pend
	    it->ackmap[ACKMAP_TOTAL_SIZE - local + it->shifts] = 1;
	    it->lastack = local;
	    if((local - it->shifts > ACKMAP_SIZE) && set == false) {
		s.schedule(&cope->h_ack_pend_, &cope->pend_event_, 0.0);
	    }
	} else {
	    if(set == false)  // 3. Old pend - duplicate*
		s.schedule(&cope->h_ack_pend_, &cope->pend_event_, 0.0);
	}
    }
    //    s.schedule(&cope->h_ack_pend_, &cope->pend_event_, 0.0);	    
}


void AckPendQueue::update_pend(int neighbor, u_int16_t local_seqnum) {
    pend_iter_t it = lookup_pend(neighbor);
    //    it->local_seqnum = local_seqnum;
    it->lastack = local_seqnum;
    it->ackmap[ACKMAP_TOTAL_SIZE - local_seqnum] = 1; // This is a problem!!!
}

/*
 * The order of bitset is from right to left, that means:
 *    bitset<8> temp(1);
 * temp should look like -> '00000001'
 *
 * However, the bitset constructor from string is from left to right.
 *    string val("1100000000");
 *    bitset<8> bs(val, 1, 8);
 * bs should look like -> '10000000'
 *
 * So we could see that the direction of increase of return value is
 * from right to left, which is just the opposite side of the paper proposed.
 * The left most bit is the largest seq_num, and the right most bit is the
 * smallest seq_num. ??????
 */

hdr_ack_t* AckPendQueue::get_pend(int neighbor) {
    pend_iter_t it = lookup_pend(neighbor);
    if(it == ack_pend_list_->end())
	return NULL;
    
    hdr_ack_t* ack_pend = (hdr_ack_t*)malloc(sizeof(hdr_ack_t));
    ack_pend->neighbor_ = neighbor;

    // and then shift the ackmap
    u_int16_t i = 0;
    while(it->ackmap[ACKMAP_TOTAL_SIZE - 1 - i] != 0) {
	i ++;
    }

    if (i > 0) {
	it->ackmap <<= i;
	it->shifts += i;
    }
    
    /*
     *  shifts = 1, lastack = 10
     *  1 2 3 4 5 6 7 8 9 10
     *  --------------------
     *  1 1 1 1 0 1 0 1 0 1
     *  ^               ^
     *  shifts          lastack
     *  That means: each time acks 8 packets, latest one is last_ack_
     */

    u_int8_t num = it->lastack - it->shifts;
    if(num >= ACKMAP_SIZE) {
	// If there are more than 9 acks in the ackmap
	bitset<ACKMAP_SIZE> temp_map(it->ackmap.to_string(),
				     0,
				     ACKMAP_SIZE);
	ack_pend->last_ack_ = it->shifts + ACKMAP_SIZE;
	ack_pend->ack_map_ = temp_map.to_ulong();
    } else if(num == 0) {
	ack_pend->ack_map_ = (u_int8_t)255;
	ack_pend->last_ack_ = it->shifts;
    } else {
	/*
	 * If there are less than 9 acks in the bitmap, then get out all the
	 * acks in the bitmap, the most left (lastack - shifts)) bits
	 * of temp_map will be filled with 1.
	 */
	bitset<ACKMAP_SIZE> marker = ~0;
	marker <<= num;
	bitset<ACKMAP_SIZE> temp_map(it->ackmap.to_string(), 0, num);
	marker |= temp_map;
	ack_pend->ack_map_ = marker.to_ulong();
	ack_pend->last_ack_ = it->lastack;
    }

    // cout << "pend: " << bitset<8>(ack_pend->ack_map_)
    // 	 << " " << (int)ack_pend->last_ack_
    // 	 << " " << ack_pend->neighbor_ << endl;
    return ack_pend;
}

